package com.hopes.bizz.packet.hj212.support;

import cn.allbs.hj212.feature.Feature;
import cn.allbs.hj212.feature.GeneratorFeature;
import cn.allbs.hj212.format.T212Generator;
import cn.allbs.hj212.format.T212Mapper;
import cn.allbs.hj212.model.CpData;
import cn.allbs.hj212.model.HjData;
import cn.allbs.hj212.model.Pollution;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.NumberUtil;
import com.hopes.bizz.packet.hj212.constant.AlarmConstant;
import com.hopes.bizz.packet.hj212.constant.HJ212Constant;
import com.hopes.bizz.packet.hj212.domain.monitor.entity.PollMonitorFactorEntity;
import lombok.SneakyThrows;

import java.io.StringWriter;
import java.math.BigDecimal;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;

/**
 * @author lyb
 * @version 1.0
 * @description: 构建212协议报文
 * @date 2023/2/27 13:52
 */
public class Hj212DataBuilder {

    private static Hj212DataBuilder instance;

    private Hj212DataBuilder() {
    }

    public static synchronized Hj212DataBuilder getInstance() {
        if (null != instance) {
            return instance;
        }
        instance = new Hj212DataBuilder();
        return instance;
    }

    /**
     * 构建默认随机实时数据上限
     */
    private final BigDecimal limitStart = BigDecimal.ZERO;
    /**
     * 构建默认随机实时数据下限
     */
    private final BigDecimal limitEnd = BigDecimal.TEN;

    private static final Random RANDOM = new Random();

    @SneakyThrows
    public String build(Hj212DataAdapter adapter) {
        HjData data = new HjData();
        data.setSt(adapter.getSt());
        data.setQn(DateUtil.format(adapter.getDataTime(), HJ212Constant.DATE_FORMAT_QN));
        data.setCn(adapter.getCn());
        data.setPw(adapter.getPw());
        data.setMn(adapter.getMn());
        CpData cp = new CpData();
        cp.setDataTime(DateUtil.format(adapter.getDataTime(), HJ212Constant.DATE_FORMAT));
        if (HJ212Constant.COMMAND_REAL_TIME_DATA.equals(data.getCn())) {
            cp.setPollution(buildDataMap(adapter));
        } else {
            cp.setPollution(buildMinDataMap(adapter));
        }
        data.setCp(cp);
        T212Mapper mapper = new T212Mapper().enableDefaultParserFeatures().enableDefaultVerifyFeatures();
        String originalMsg = mapper.writeDataAsString(data);
        StringBuilder sb = new StringBuilder();
        sb.append(originalMsg, 6, originalMsg.length() - 6);
        String flagSouStr = "CP=&&";
        String flagTarStr = "Flag=4;CP=&&";
        sb.replace(sb.indexOf(flagSouStr), sb.indexOf(flagSouStr) + flagSouStr.length(), flagTarStr);

        Map<String, String> substringMap = adapter.getSubstringMap();
        for (String key : substringMap.keySet()) {
            replace(sb, key, substringMap.get(key));
        }

        String msg = sb.toString();
        sb.setLength(0);

        StringWriter writer = new StringWriter();
        T212Generator generator = new T212Generator(writer);
        generator.setGeneratorFeature(Feature.collectFeatureDefaults(GeneratorFeature.class));
        generator.writeHeader();
        generator.writeDataAndLenAndCrc(msg.toCharArray());
        generator.writeFooter();
        return writer.toString();
    }

    private void replace(StringBuilder builder, String source, String target) {
        while (builder.indexOf(source) != -1) {
            builder.replace(builder.indexOf(source), builder.indexOf(source) + source.length(), target);
        }
    }

    private Map<String, Pollution> buildDataMap(Hj212DataAdapter adapter) {
        Map<String, Pollution> pollutionMap = new LinkedHashMap<>();
        List<PollMonitorFactorEntity> dataList = adapter.getDataList();

        if (CollUtil.isNotEmpty(dataList)) {
            dataList.forEach(factor -> {
                Pollution pollution = new Pollution();
                pollution.setRtd(getRtdValue(adapter, factor));
                // 计算折算值
                if (adapter.getZsFlag()) {
                    pollution.setZsRtd(getRtdValue(adapter, factor));
                }
                pollutionMap.put(factor.getFactorCode(), pollution);
            });
        }
        return pollutionMap;
    }

    private Map<String, Pollution> buildMinDataMap(Hj212DataAdapter adapter) {
        Map<String, Pollution> pollutionMap = new LinkedHashMap<>();
        List<PollMonitorFactorEntity> dataList = adapter.getDataList();

        if (CollUtil.isNotEmpty(dataList)) {
            dataList.forEach(factor -> {
                Pollution pollution = new Pollution();
                pollution.setFlag(HJ212Constant.FLAG);
                pollution.setCou(getRtdValue(adapter, factor));
                pollution.setMin(getRtdValue(adapter, factor));
                pollution.setAvg(getRtdValue(adapter, factor));
                pollution.setMax(getRtdValue(adapter, factor));

                // 计算折算值
                if (adapter.getZsFlag()) {
                    pollution.setZsMin(getRtdValue(adapter, factor));
                    pollution.setZsAvg(getRtdValue(adapter, factor));
                    pollution.setZsMax(getRtdValue(adapter, factor));
                }
                pollutionMap.put(factor.getFactorCode(), pollution);
            });
        }
        return pollutionMap;
    }

    private BigDecimal getRtdValue(Hj212DataAdapter adapter, PollMonitorFactorEntity factor) {
        Map<String, Integer> alarmMap = adapter.getFactorAlarmMap();
        if (CollUtil.isNotEmpty(alarmMap)) {
            Integer alarmType = alarmMap.get(factor.getFactorCode());
            if (alarmType != null) {
                if (AlarmConstant.ALARM_TYPE_EXCEED.equals(alarmType)) {
                    // 构建超标
                    return getAlarmData(factor);
                } else if (AlarmConstant.ALARM_TYPE_WARN.equals(alarmType)) {
                    // 构建预警
                    return getWarnData(factor);
                }
            }
        }
        return getNormalData(factor);
    }

    private BigDecimal getNormalData(PollMonitorFactorEntity factor) {
        if (factor.getIsHaveRange()) {
            return getRoundData(factor.getMinRange(), factor.getMaxRange());
        }
        return getRoundData(this.limitStart, this.limitEnd);
    }

    private BigDecimal getWarnData(PollMonitorFactorEntity factor) {
        BigDecimal maxWarn = factor.getMaxWarnAlarm();
        BigDecimal minWarn = factor.getMinWarnAlarm();
        BigDecimal maxAlarm = factor.getMaxAlarm() != null ? factor.getMaxAlarm() : this.limitEnd;
        BigDecimal minAlarm = factor.getMinAlarm() != null ? factor.getMinAlarm() : this.limitStart;
        if (maxWarn != null && minWarn != null) {
            // 随机
            if (RANDOM.nextInt() % 2 == 0) {
                return getRoundData(maxWarn, maxAlarm);
            } else {
                return getRoundData(minAlarm, minWarn);
            }
        }

        if (maxWarn != null) {
            return getRoundData(maxWarn, maxAlarm);
        }

        if (minWarn != null) {
            return getRoundData(minAlarm, minWarn);
        }
        return getNormalData(factor);
    }

    private BigDecimal getAlarmData(PollMonitorFactorEntity factor) {
        BigDecimal maxAlarm = factor.getMaxAlarm();
        BigDecimal minAlarm = factor.getMinAlarm();
        if (maxAlarm != null && minAlarm != null) {
            // 随机最大报警，最小报警
            if (RANDOM.nextInt() % 2 == 0) {
                return getRoundData(maxAlarm, maxAlarm.add(this.limitEnd));
            } else {
                return getRoundData(this.limitStart, minAlarm);
            }
        }

        if (maxAlarm != null) {
            return getRoundData(maxAlarm, maxAlarm.add(this.limitEnd));
        }

        if (minAlarm != null) {
            return getRoundData(this.limitStart, minAlarm);
        }
        return getNormalData(factor);
    }

    private BigDecimal getRoundData(BigDecimal left, BigDecimal right) {
        if (left.compareTo(right) >= 0 ) {
            right = left.add(BigDecimal.ONE);
        }
        return NumberUtil.round(RANDOM.doubles(1, left.doubleValue(), right.doubleValue()).boxed().findAny().get(), 3);
    }

}
